//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================

#include <ddk.h>

#include "tinterrupt.h"
#include "tserial.h"
#include "ddk_tst_print.h"

//////////////////////////////////////////////////////////////////////////
const uint16_t    c_uKbdPort  = 0x60;
const uint16_t    c_uCOM1Port = 0x3F8;
const uint16_t    c_uCOM2Port = 0x2F8;
const uint16_t    c_uLPT1Port = 0x378;

EXTERN IDeviceDriver *g_pKbdDriver;
extern void CDECL KeyboardIsr(
        irq_t irq, void *pvDevice, InterruptContext *pContext);

char g_szFileName[20];
DzEvent *g_pEvent;
uint_t g_uState;

int __cdecl tprintf(char const *fmt,...)
{
    va_list ap;

    va_start(ap, fmt);

    if (strlen(g_szFileName))
        DDK_PRINT_LINE_START(g_szFileName);

    int nLength = _vprintf(_print, fmt, ap);

    if (strlen(g_szFileName))
        DDK_PRINT_LINE_END();

    va_end(ap);

    return nLength;
}
/////////////////////////////////////////////////////////////////////////
//
// TKbdIsr01
//
void CDECL TKbdIsr01(irq_t irq, void *pvDevice, InterruptContext *pContext)
{
    Inb(c_uKbdPort);
    tprintf("TKbdIsr01:.\n");
    return;
}
//
// TKbdIsr02
//
void CDECL TKbdIsr02(irq_t irq, void *pvDevice, InterruptContext *pContext)
{
    Inb(c_uKbdPort);
    tprintf("Now running TKbdIsr02.\n");
    return;
}
//
// TKbdIsr03
//
void CDECL TKbdIsr03(irq_t irq, void *pvDevice, InterruptContext *pContext)
{
    Inb(c_uKbdPort);
    tprintf("Now running TKbdIsr03.\n");
    return;
}
//
// TKbdIsr04
//
void CDECL TKbdIsr04(irq_t irq, void *pvDevice, InterruptContext *pContext)
{
    Inb(c_uKbdPort);
    tprintf("Now running TKbdIsr04.\n");
    return;
}
//
// TSerialIsr01
//
void CDECL TSerialIsr01(irq_t irq, void *pvDevice, InterruptContext *pContext)
{
    uint16_t uIoBase = ((TSerialDriver *)pvDevice)->m_uIoBase;
    uint_t uIntStat = UART_READ_INT_STAT(uIoBase);
    tprintf("TSerialIsr01: IOBase=0x%X, Interrupt status is 0x%X.\n",
        uIoBase, uIntStat);
}
//
// TSerialIsr02
//
void CDECL TSerialIsr02(irq_t irq, void *pvDevice, InterruptContext *pContext)
{
    uint16_t uIoBase = ((TSerialDriver *)pvDevice)->m_uIoBase;
    uint_t uIntStat = UART_READ_INT_STAT(uIoBase);
    tprintf("TSerialIsr02: IOBase=0x%X, Interrupt status is 0x%X.\n",
        uIoBase, uIntStat);
}
//
// TSerialIsr03
//
void CDECL TSerialIsr03(irq_t irq, void *pvDevice, InterruptContext *pContext)
{
    uint16_t uIoBase = ((TSerialDriver *)pvDevice)->m_uIoBase;
    uint_t uIntStat = UART_READ_INT_STAT(uIoBase);
    tprintf("TSerialIsr03: IOBase=0x%X, Interrupt status is 0x%X.\n",
        uIoBase, uIntStat);
}
//
// TSerialIsr04
//
void CDECL TSerialIsr04(irq_t irq, void *pvDevice, InterruptContext *pContext)
{
    uint16_t uIoBase = ((TSerialDriver *)pvDevice)->m_uIoBase;
    uint_t uIntStat = UART_READ_INT_STAT(uIoBase);
    tprintf("TSerialIsr04: IOBase=0x%X, Interrupt status is 0x%X.\n",
        uIoBase, uIntStat);
}
//
// TKbdIsrPrint
//
void CDECL TKbdIsrPrint(irq_t irq, void *pvDevice, InterruptContext *pContext)
{
    Inb(c_uKbdPort);
    kprintf("TKbdIsrPrint:I am going to do....");
    for (int i = 0; i < 1000; i++) {
        kprintf(".");
        for (uint_t j = 0; j < 500000; j++);
    }
    kprintf("\nTKbdIsrPrint finish pirnt.\n");
}
//
// TSerialIsrPrint
//
void CDECL TSerialIsrPrint(irq_t irq, void *pvDevice,
    InterruptContext *pContext)
{
    UART_READ_INT_STAT(((TSerialDriver *)pvDevice)->m_uIoBase);

    kprintf("TSerialIsrPrint:I am going to do....");
    for (int i = 0; i < 1000; i++) {
        kprintf(".");
        for (uint_t j = 0; j < 500000; j++);
    }
    kprintf("\nTSerialIsrPrint finish pirnt.\n");
}
//
// TKbdIsrNotify0
//
void CDECL TKbdIsrNotify0(irq_t irq, void *pvDevice,
    InterruptContext *pContext)
{
    Inb(c_uKbdPort);
    g_pEvent->NotifyByIsr();
    kprintf("TKbdIsrNotify0 run.\n");
}
//
// TKbdIsrNotifyClear
//
void CDECL TKbdIsrNotifyClear(irq_t irq, void *pvDevice,
    InterruptContext *pContext)
{
    Inb(c_uKbdPort);
    g_pEvent->NotifyByIsr();
    kprintf("TKbdIsrNotify0 run.\n");
    g_pEvent->Clear();
}
//
// TKbdIsrNotify1
//
void CDECL TSerialIsrNotify1(irq_t irq, void *pvDevice,
    InterruptContext *pContext)
{
    UART_READ_INT_STAT(((TSerialDriver *)pvDevice)->m_uIoBase);
    if (g_uState) {
        g_pEvent->NotifyByIsr(g_uState);
        kprintf("TKbdIsrNotify1 run, the uState=%d.\n", g_uState);
    }
    else {
        kprintf("NotifyByIsr no run.\n");
    }
}
//
// TSerialIsrNotifyClear
//
void CDECL TSerialIsrNotifyClear(irq_t irq, void *pvDevice,
    InterruptContext *pContext)
{
    UART_READ_INT_STAT(((TSerialDriver *)pvDevice)->m_uIoBase);
    if (g_uState) {
        g_pEvent->NotifyByIsr(g_uState);
        kprintf("TSerialIsrNotifyClear run, the uState=%d.\n", g_uState);
        g_pEvent->Clear();
    }
    else {
        kprintf("NotifyByIsr no run.\n");
    }
}
//
// TCommonIsr1
//
void CDECL TCommonIsr1(irq_t irq, void *pvDevice, InterruptContext *pContext)
{
    tprintf("Now running TCommonIsr1, and the current irq is %d\n",irq);

    if (irq == 1)
        Inb(c_uKbdPort);

    return;
}
/////////////////////////////////////////////////////////////////////////////
IDeviceDriver     *g_pTInterrupt = NULL;
//
// The Interrupt Test driver's classfactory
//
EXTERN IDeviceDriver * CDECL CreateTInterrupt(uint_t uDeviceNo, void *pvParameter)
{
    g_pTInterrupt = new TInterrupt;
    if (!g_pTInterrupt) {
        return NULL;
    }

    ECode ec = ((TInterrupt *)g_pTInterrupt)->Initialize();
    if (FAILED(ec)) {
        delete g_pTInterrupt;
        g_pTInterrupt = NULL;
        return NULL;
    }

    g_pTInterrupt->AddRef();

    return g_pTInterrupt;
}

ECode TInterrupt::Initialize()
{
    m_pTSerialDriver1 = CreateTSerial(0, (void *)&c_uCOM1Port);
    if (!m_pTSerialDriver1) {
        kprintf("Failed to find COM1 Driver");
        return E_FAIL;
    }

    m_iplArray[0] = IPL0;
    m_iplArray[1] = IPL1;
    m_iplArray[2] = IPL2;
    m_iplArray[3] = IPL3;
    m_iplArray[4] = IPL4;
    m_iplArray[5] = IPL5;
    m_iplArray[6] = IPL6;
    m_iplArray[7] = IPL7;
    m_iplArray[8] = IPL8;
    m_iplArray[9] = IPL9;
    m_iplArray[10] = IPL10;
    m_iplArray[11] = IPL11;
    m_iplArray[12] = IPL12;
    m_iplArray[13] = IPL13;
    m_iplArray[14] = IPL14;
    m_iplArray[15] = IPL15;
    m_iplArray[16] = IPL16;
    m_iplArray[17] = IPL17;
    m_iplArray[18] = IPL18;
    m_iplArray[19] = IPL19;
    m_iplArray[20] = IPL20;
    m_iplArray[21] = IPL21;
    m_iplArray[22] = IPL22;
    m_iplArray[23] = IPL23;
    m_iplArray[24] = IPL24;
    m_iplArray[25] = IPL25;
    m_iplArray[26] = IPL26;
    m_iplArray[27] = IPL27;
    m_iplArray[28] = IPL28;
    m_iplArray[29] = IPL29;
    m_iplArray[30] = IPL30;
    m_iplArray[31] = IPL31;
    m_iplArray[32] = IPL_Base;

    m_isrArray[ISR_NULL] = NULL;
    m_isrArray[KbdNormalIsr] = &KeyboardIsr;
    m_isrArray[KbdIsr01] = &TKbdIsr01;
    m_isrArray[KbdIsr02] = &TKbdIsr02;
    m_isrArray[KbdIsr03] = &TKbdIsr03;
    m_isrArray[KbdIsr04] = &TKbdIsr04;
    m_isrArray[SerialIsr01] = &TSerialIsr01;
    m_isrArray[SerialIsr02] = &TSerialIsr02;
    m_isrArray[SerialIsr03] = &TSerialIsr03;
    m_isrArray[SerialIsr04] = &TSerialIsr04;
    m_isrArray[CommonIsr1] = &TCommonIsr1;
    m_isrArray[SerialIsrPrint] = &TSerialIsrPrint;
    m_isrArray[KbdIsrPrint] = &TKbdIsrPrint;
    m_isrArray[KbdIsrNotify0]    = &TKbdIsrNotify0;
    m_isrArray[SerialIsrNotify1] = &TSerialIsrNotify1;
    m_isrArray[KbdIsrNotifyClear]    = &TKbdIsrNotifyClear;
    m_isrArray[SerialIsrNotifyClear] = &TSerialIsrNotifyClear;

    return NOERROR;
}

uint8_t TInterrupt::FindIplNo(ipl_t ipl)
{
    uint8_t i;
    for (i = 0; i < 33; i++) {
        if (m_iplArray[i] == ipl)
            break;
    }
    return i;
}

TInterrupt::~TInterrupt()
{
    if (m_pTSerialDriver1) {
        m_pTSerialDriver1->Release();
    }
}

ECode TInterrupt::Read(
    /* [in] */ UInt64 u64Offset,
    /* [in] */ UInt32 bytesToRead,
    /* [out] */ EzByteBuf buffer,
    /* [out] */ IEvent * * ppCompletionEvent)
{
    return E_NOT_IMPLEMENTED;
}

ECode TInterrupt::Write(
    /* [in] */ UInt64 u64Offset,
    /* [in] */ EzByteBuf buffer,
    /* [out] */ UInt32 * pBytesWritten,
    /* [out] */ IEvent * * ppCompletionEvent)
{
    return E_NOT_IMPLEMENTED;
}

ECode TInterrupt::Control(
    /* [in] */ Handle32 nControlCode,
    /* [in] */ EzByteBuf inBuffer,
    /* [out] */ EzByteBuf * pOutBuffer,
    /* [out] */ IEvent * * ppCompletionEvent)
{
    if ((nControlCode < 0) || (nControlCode > 100)
        || (ppCompletionEvent && *ppCompletionEvent)) {
        return E_INVALID_ARGUMENT;
    }

    ECode ec = NOERROR;
    PTInterruptInfo pTInterruptInfo = (PTInterruptInfo)(char *)inBuffer;

    switch (nControlCode) {
        case 0 :{
            m_pTSerialDriver1->Control(1,EZBYTEBUF_NULL,EZBYTEBUF_NULL,NULL);
            break;
        }
        case 1 :{
            tprintf("RegisterIsr:irq=%d, ipl=%d, isrNo=%d.\n",
                pTInterruptInfo->irq, pTInterruptInfo->uIplNo,
                pTInterruptInfo->IsrNo);
            TRegisterIsr(pTInterruptInfo);
            break;
        }
        case 2 :{
            DzUnregisterIsr(pTInterruptInfo->irq);
            tprintf("UnregisterIsr:irq=%d.\n", pTInterruptInfo->irq);
            break;
        }
        case 3 :{
            tprintf("EnableIrq:irq=%d.\n", pTInterruptInfo->irq);
            DzEnableIrq(pTInterruptInfo->irq);
            break;
        }
        case 4 :{
            tprintf("DisableIrq:irq=%d.\n", pTInterruptInfo->irq);
            DzDisableIrq(pTInterruptInfo->irq);
            break;
        }
        case 5 :{
            ipl_t ipl = DzRaiseCurrentIpl(m_iplArray[pTInterruptInfo->uIplNo]);
            uint8_t uIplNo = FindIplNo(ipl);
            tprintf("RaiseCurrentIpl:ipl=%d, ret=%d.\n",
                pTInterruptInfo->uIplNo, uIplNo);
            break;
        }
        case 6 :{
            ipl_t ipl = DzLowerCurrentIpl(m_iplArray[pTInterruptInfo->uIplNo]);
            uint8_t uIplNo = FindIplNo(ipl);
            tprintf("LowerCurrentIpl:ipl=%d, ret=%d.\n",
                pTInterruptInfo->uIplNo, uIplNo);
            break;
        }
        case 7 :{
            //simulate pressing keyboard
            tprintf("Keyboard type a char is '$'.\n");
            Outb(0x64,0xD2);
            Outb(0x60,'$');
            break;
        }
        case 8 :{
            strcpy(g_szFileName, (char *)inBuffer);
            break;
        }

        case 10:{
            if (m_pEvent) {
                delete m_pEvent;
            }
            else {
                kprintf("There is no event object.\n");
                ec = E_FAIL;
            }

            m_pEvent = NULL;
            g_pEvent = NULL;

            break;
        }
        // manual, unsignal
        case 11:{
            if (m_pEvent) {
                kprintf("There is has a event object.\n");
                ec = S_FALSE;
            }
            else {
                m_pEvent = new DzEvent(TRUE, UNSIGNALED);
                if (NULL == m_pEvent) {
                    ec = E_OUT_OF_MEMORY;
                }
                else {
                    ec = NOERROR;
                    g_pEvent = m_pEvent;
                }
            }

            break;
        }

        // manual, signal
        case 12:{
            if (m_pEvent) {
                kprintf("There is has a event object.\n");
                ec = S_FALSE;
            }
            else {
                m_pEvent = new DzEvent(TRUE, SIGNALED);
                if (NULL == m_pEvent) {
                    ec = E_OUT_OF_MEMORY;
                }
                else {
                    ec = NOERROR;
                    g_pEvent = m_pEvent;
                }
            }

            break;
        }

        // auto, unsignaled
        case 13:{
            if (m_pEvent) {
                kprintf("There is has a event object.\n");
                ec = S_FALSE;
            }
            else {
                m_pEvent = new DzEvent(FALSE, UNSIGNALED);
                if (NULL == m_pEvent) {
                    ec = E_OUT_OF_MEMORY;
                }
                else {
                    ec = NOERROR;
                    g_pEvent = m_pEvent;
                }
            }

            break;
        }

        // auto, signaled
        case 14:{
            if (m_pEvent) {
                kprintf("There is has a event object.\n");
                ec = S_FALSE;
            }
            else {
                m_pEvent = new DzEvent(FALSE, SIGNALED);
                if (NULL == m_pEvent) {
                    ec = E_OUT_OF_MEMORY;
                }
                else {
                    ec = NOERROR;
                    g_pEvent = m_pEvent;
                }
            }

            break;
        }

        case 15:{
            uint_t uState;

            ec = m_pEvent->Wait(&uState);

            if ((uint_t)pOutBuffer->GetCapacity() >= sizeof(uint_t)) {
                *(uint_t *)(char *)ebbOutData = uState;
                pOutBuffer->SetUsed(sizeof(uint_t));
            }

            break;
        }

        case 16:{
            uint_t uWaitTime = *(uint_t *)(char *)inBuffer;
            uint_t uState;

            ec = m_pEvent->TryWait(uWaitTime, &uState);

            if ((uint_t)pOutBuffer->GetCapacity() >= sizeof(uint_t)) {
                *(uint_t *)(char *)ebbOutData = uState;
                pOutBuffer->SetUsed(sizeof(uint_t));
            }

            break;
        }
        case 17:{
            g_uState = *(uint_t *)(char *)inBuffer;
            break;
        }
        case 18:{
            m_pEvent->Clear();
            break;
        }

        default:{
            return E_NOT_IMPLEMENTED;
        }
    }
    return ec;
}

void TInterrupt::TRegisterIsr(PTInterruptInfo pTInterruptInfo)
{
    IDeviceDriver *pDriver = NULL;
    if (pTInterruptInfo->irq == 0x04)
        pDriver = m_pTSerialDriver1;
    else if (pTInterruptInfo->IsrNo == KbdNormalIsr)
        pDriver = g_pKbdDriver;

    DzRegisterIsr(pTInterruptInfo->irq, m_iplArray[pTInterruptInfo->uIplNo],
        m_isrArray[pTInterruptInfo->IsrNo], pDriver);
}
